package com.mybatis.boost.mapper.util.sql;

import com.mybatis.boost.mapper.model.MapperException;
import org.apache.ibatis.scripting.xmltags.ChooseSqlNode;
import org.apache.ibatis.scripting.xmltags.IfSqlNode;
import org.apache.ibatis.scripting.xmltags.SqlNode;
import org.apache.ibatis.scripting.xmltags.TextSqlNode;
import tk.mybatis.mapper.entity.Config;
import tk.mybatis.mapper.entity.EntityColumn;
import tk.mybatis.mapper.mapperhelper.EntityHelper;

import java.util.Optional;
import java.util.Set;

import static com.google.common.collect.Lists.newArrayList;
import static com.mybatis.boost.mapper.SingleBaseMapper.PARAM_DEFAULT;
import static java.util.stream.Collectors.toSet;

/**
 * INSERT-动态SQL生成辅助工具
 *
 * @author jearton
 * @since 2017/2/9
 */
public abstract class InsertSqlUtil {
    /**
     * 获取主键列，最多只能有一个
     *
     * @param entityClass 实体类
     */
    public static Optional<EntityColumn> getIdentityColumn(final Class<?> entityClass) {
        Set<EntityColumn> columnList = EntityHelper.getColumns(entityClass).stream()
                .filter(EntityColumn::isInsertable)
                .filter(EntityColumn::isIdentity)
                .collect(toSet());
        if (columnList.size() == 0) {
            return Optional.empty();
        }
        if (columnList.size() > 1) {
            throw new MapperException("Too many identity columns defined in " + entityClass.getCanonicalName());
        }
        return Optional.of(columnList.iterator().next());
    }

    /**
     * 用于生成主键的动态SQL，将放入SelectKey标签
     *
     * @param generator 获取主键的SQL
     */
    public static SqlNode getSelectKeySql(EntityColumn column, String generator) {
        SqlNode ifSqlNode = new IfSqlNode(new TextSqlNode(generator), column.getProperty() + " == null");
        SqlNode defaultSqlNode = new TextSqlNode("SELECT " + SqlUtil.getColumnHolder(column));
        return new ChooseSqlNode(newArrayList(ifSqlNode), defaultSqlNode);
    }

    /**
     * 获取预设UUID的动态SQL代码
     *
     * @param entityClass        实体类
     * @param uniqueIdExpression 获取唯一ID的表达式
     * @see Config#getUUID()
     */
    public static String insertUniqueId(final Class<?> entityClass, final String uniqueIdExpression) {
        return EntityHelper.getColumns(entityClass).stream()
                .filter(EntityColumn::isInsertable)
                .filter(EntityColumn::isUuid)
                .map(column -> _getUniqueIdSeg(column, PARAM_DEFAULT, uniqueIdExpression))
                .reduce(String::concat)
                .orElse("");
    }

    /**
     * insert into tableName - 动态表名
     *
     * @param entityClass      实体类
     * @param defaultTableName 默认表名
     */
    public static String insertIntoTable(Class<?> entityClass, String defaultTableName) {
        return "INSERT INTO " + SqlUtil.getDynamicTableName(entityClass, defaultTableName) + " ";
    }

    /**
     * insert into tableName
     *
     * @param tableName 表名
     */
    public static String insertBatchIntoTable(String tableName) {
        return "INSERT INTO " + tableName + " ";
    }

    /**
     * insert ignore into tableName - 动态表名
     *
     * @param entityClass      实体类
     * @param defaultTableName 默认表名
     */
    public static String insertIgnoreIntoTable(Class<?> entityClass, String defaultTableName) {
        return "INSERT IGNORE INTO " + SqlUtil.getDynamicTableName(entityClass, defaultTableName) + " ";
    }

    /**
     * insert ignore into tableName
     *
     * @param tableName 表名
     */
    public static String insertBatchIgnoreIntoTable(String tableName) {
        return "INSERT IGNORE INTO " + tableName + " ";
    }

    /**
     * insert指定列
     *
     * @param entityClass 实体类
     */
    public static String insertColumns(Class<?> entityClass) {
        Set<EntityColumn> columnList = EntityHelper.getColumns(entityClass);
        StringBuilder sql = new StringBuilder();

        sql.append("<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">");
        for (EntityColumn column : columnList) {
            if (!column.isInsertable()) {
                continue;
            }
            //当该列在数据库中明确定义为not null，则加上!=null的判断
            if (SqlUtil.isNotNull(column)) {
                sql.append(SqlUtil.getIfNotNull(column, column.getColumn() + ","));
            } else {
                sql.append(column.getColumn()).append(",");
            }
        }
        sql.append("</trim>");
        return sql.toString();
    }

    public static String insertValues(Class<?> entityClass) {
        Set<EntityColumn> columnList = EntityHelper.getColumns(entityClass);
        StringBuilder sql = new StringBuilder();

        sql.append("<trim prefix=\"VALUES(\" suffix=\")\" suffixOverrides=\",\">");
        for (EntityColumn column : columnList) {
            if (!column.isInsertable()) {
                continue;
            }
            //当该列在数据库中明确定义为not null，必须加上!=null的判断
            String content = SqlUtil.getColumnHolderWithComma(column);
            if (SqlUtil.isNotNull(column)) {
                sql.append(SqlUtil.getIfNotNull(column, content));
            } else {
                sql.append(content);
            }
        }
        sql.append("</trim>");
        return sql.toString();
    }

    public static String insertBatchColumns(Class<?> entityClass) {
        Set<EntityColumn> columnList = EntityHelper.getColumns(entityClass);
        StringBuilder sql = new StringBuilder();

        sql.append("<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">");
        columnList.stream()
                .filter(column -> column.isInsertable() && !column.isIdentity())
                .forEach(column -> sql.append(column.getColumn()).append(","));
        sql.append("</trim>");
        return sql.toString();
    }

    public static String insertBatchValues(final Class<?> entityClass, final String uniqueIdExpression) {
        Set<EntityColumn> columnList = EntityHelper.getColumns(entityClass);
        StringBuilder sql = new StringBuilder();

        sql.append(" VALUES ");
        sql.append("<foreach collection=\"list\" item=\"record\" separator=\",\" >");
        columnList.stream()
                .filter(column -> column.isInsertable() && column.isUuid())
                .forEach(column -> sql.append(_getUniqueIdSeg(column, "record", uniqueIdExpression)));
        sql.append("<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">");
        columnList.stream()
                .filter(column -> column.isInsertable() && !column.isIdentity())
                .forEach(column -> sql.append(column.getColumnHolder("record")).append(","));
        sql.append("</trim>");
        sql.append("</foreach>");
        return sql.toString();
    }

    /**
     * 回写唯一键的值
     * <p>
     * 返回格式：<if test='trade_no == null and _parameter.trade_no = uniqueIdExpression'/>
     */
    private static String _getUniqueIdSeg(final EntityColumn column, final String entityName, final String uniqueIdExpression) {
        String template = "<if test='%s == null and %s = %s'/>";
        String property = entityName + "." + column.getProperty();
        return String.format(template, property, property, uniqueIdExpression);
    }
}
